From 93f056d3a1f2873a19c6a42b1c31fde2f87f4763 Mon Sep 17 00:00:00 2001 From: Nicolas BESNARD Date: Tue, 26 Nov 2024 16:29:27 +0000 Subject: [PATCH] reconfigure: implement DHCP reconfiguration MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Problem: To update DHCP parameters/options, the process needed to be restarted with the new arguments. It makes odhcp6c not very flexible, making it impossible to keep track of the total packet statistics. Solution: Add a new ubus method to reconfigure DHCP without restarting the process. Signed-off-by: Nicolas BESNARD Signed-off-by: Paul Donald Link: https://github.com/openwrt/odhcp6c/pull/106 Signed-off-by: Álvaro Fernández Rojas --- README | 42 ++++++++++++ src/dhcpv6.c | 4 +- src/odhcp6c.c | 167 ++++++++++++++++++++++++++++++++++++++++------ src/odhcp6c.h | 16 ++++- src/ubus.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 386 insertions(+), 22 deletions(-) diff --git a/README b/README index e79fea1..1afed15 100644 --- a/README +++ b/README @@ -85,3 +85,45 @@ Environment: * RA_RETRANSMIT ND Retransmit time * AFTR The DS-Lite AFTR domain name * MAPE / MAPT / LW4O6 Softwire rules for MAPE, MAPT and LW4O6 + +** Ubus Integration ** + +Build with ENABLE_UBUS flag to connect odhcp6c to ubus. Object is registered at : "odhcp6c.{ifname}." + +Events are emitted whenever the DHCPv6 state changes and can replace the use of a state script. The variables are the same as those defined in the State Script section. + +The following RPC methods are available: + +* *get_state()* : Returns the DHCPv6 state + - OUT : see State Script section +* *get_statistics()* : Returns the packet statistics + - OUT : + - *dhcp_solicit* : Total number of SOLICIT messages sent + - *dhcp_advertise* : Total number of ADVERTISE messages received + - *dhcp_request* : Total number of REQUEST messages sent + - *dhcp_confirm* : Total number of CONFIRM messages sent + - *dhcp_renew* : Total number of RENEW messages sent + - *dhcp_rebind* : Total number of REBIND messages sent + - *dhcp_reply* : Total number of REPLY messages received + - *dhcp_release* : Total number of RELEASE messages sent + - *dhcp_decline* : Total number of DECLINE messages sent + - *dhcp_reconfigure* : Total number of RECONFIGURE messages received + - *dhcp_information_request* : Total number of INFORMATION-REQUEST messages sent + - *dhcp_discarded_packets* : Total number of discarded DHCP packets + - *dhcp_transmit_failures* : Total number of DHCP messages that failed to be transmitted +* *reset_statistics()* : Reset packet statistics +* *reconfigure_dhcp(IN)* : Reconfigure DHCP settings + - IN (optional): + - *dscp* (int) : DSCP value used for DHCP packets + - *release* (bool) : Send a RELEASE message on exit/reset + - *sol_timeout* (int) : Maximum timeout for DHCPv6-SOLICIT + - *sk_prio* (int) : Packet kernel priority + - *opt_requested* (int[]) : Options to be requested + - *opt_strict* (bool) : Do not request any options except those specified + - *opt_reconfigure* (bool) : Send Accept Reconfigure option + - *opt_fqdn* (bool) : Send Client FQDN option + - *opt_unicast* (bool) : Ignore Server Unicast option + - *opt_send* (string[]) : Options to be sent + - *req_addresses* (string{try|force|none}) : Request addresses + - *req_prefixes* (int) : Request Prefixes (0 = auto) + - *stateful_only* (bool) : Discard advertisements without any address or prefix proposed diff --git a/src/dhcpv6.c b/src/dhcpv6.c index dc48fe0..50ec027 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -79,7 +79,7 @@ static reply_handler dhcpv6_handle_reconfigure; static int dhcpv6_commit_advert(void); // RFC 3315 - 5.5 Timeout and Delay values -static struct dhcpv6_retx dhcpv6_retx[_DHCPV6_MSG_MAX] = { +static const struct dhcpv6_retx dhcpv6_retx_default[_DHCPV6_MSG_MAX] = { [DHCPV6_MSG_UNKNOWN] = {false, 1, 120, 0, "", dhcpv6_handle_reconfigure, NULL, false, 0, 0, 0, {0, 0, 0}, 0, 0, 0, -1, 0}, [DHCPV6_MSG_SOLICIT] = {true, 1, DHCPV6_SOL_MAX_RT, 0, "SOLICIT", @@ -95,6 +95,7 @@ static struct dhcpv6_retx dhcpv6_retx[_DHCPV6_MSG_MAX] = { [DHCPV6_MSG_INFO_REQ] = {true, 1, DHCPV6_INF_MAX_RT, 0, "INFOREQ", dhcpv6_handle_reply, NULL, false, 0, 0, 0, {0, 0, 0}, 0, 0, 0, -1, 0}, }; +static struct dhcpv6_retx dhcpv6_retx[_DHCPV6_MSG_MAX] = {0}; // Sockets static int sock = -1; @@ -403,6 +404,7 @@ void dhcpv6_reset_stats(void) int init_dhcpv6(const char *ifname, unsigned int options, int sk_prio, int sol_timeout, unsigned int dscp) { + memcpy(dhcpv6_retx, dhcpv6_retx_default, sizeof(dhcpv6_retx)); client_options = options; dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_timeo = sol_timeout; diff --git a/src/odhcp6c.c b/src/odhcp6c.c index 2ac55b5..c5b8cb4 100644 --- a/src/odhcp6c.c +++ b/src/odhcp6c.c @@ -39,6 +39,9 @@ #include "ra.h" #include "ubus.h" +#define DHCPV6_FD_INDEX 0 +#define UBUS_FD_INDEX 1 + #ifndef IN6_IS_ADDR_UNIQUELOCAL #define IN6_IS_ADDR_UNIQUELOCAL(a) \ ((((__const uint32_t *) (a))[0] & htonl (0xfe000000)) \ @@ -66,6 +69,14 @@ static int urandom_fd = -1, allow_slaac_only = 0; static bool bound = false, release = true, ra = false; static time_t last_update = 0; static char *ifname = NULL; +static unsigned int dscp = 0; +static int sol_timeout = DHCPV6_SOL_MAX_RT; +static int sk_prio = 0; +bool stateful_only_mode = 0; +static enum odhcp6c_ia_mode ia_na_mode = IA_MODE_TRY; +static enum odhcp6c_ia_mode ia_pd_mode = IA_MODE_NONE; +static unsigned int client_options = DHCPV6_CLIENT_FQDN | DHCPV6_ACCEPT_RECONFIGURE; +static unsigned int oro_user_cnt = 0; static unsigned int script_sync_delay = 10; static unsigned int script_accu_delay = 1; @@ -175,22 +186,15 @@ int main(_unused int argc, char* const argv[]) uint8_t buf[134], *o_data; char *optpos; uint16_t opttype; - enum odhcp6c_ia_mode ia_na_mode = IA_MODE_TRY; - enum odhcp6c_ia_mode ia_pd_mode = IA_MODE_NONE; - bool stateful_only_mode = 0; struct odhcp6c_opt *opt; int ia_pd_iaid_index = 0; - int sk_prio = 0; - int sol_timeout = DHCPV6_SOL_MAX_RT; int verbosity = 0; bool help = false, daemonize = false; int logopt = LOG_PID; int c; int res = -1; - unsigned int client_options = DHCPV6_CLIENT_FQDN | DHCPV6_ACCEPT_RECONFIGURE; unsigned int ra_options = RA_RDNSS_DEFAULT_LIFETIME; unsigned int ra_holdoff_interval = RA_MIN_ADV_INTERVAL; - unsigned int dscp = 0; bool terminate = false; while ((c = getopt(argc, argv, "S::DN:V:P:FB:c:i:r:Ru:Ux:s:kK:t:C:m:Lhedp:fav")) != -1) { @@ -467,7 +471,6 @@ int main(_unused int argc, char* const argv[]) } if ((urandom_fd = open("/dev/urandom", O_CLOEXEC | O_RDONLY)) < 0 || - init_dhcpv6(ifname, client_options, sk_prio, sol_timeout, dscp) || ra_init(ifname, &ifid, ra_options, ra_holdoff_interval) || script_init(script, ifname)) { syslog(LOG_ERR, "failed to initialize: %s", strerror(errno)); @@ -477,17 +480,11 @@ int main(_unused int argc, char* const argv[]) struct pollfd fds[2] = {0}; int nfds = 0; - int dhcpv6_socket = dhcpv6_get_socket(); int mode = DHCPV6_UNKNOWN; enum dhcpv6_msg msg_type = DHCPV6_MSG_UNKNOWN; - if (dhcpv6_socket < 0) { - syslog(LOG_ERR, "Invalid dhcpv6 file descriptor"); - return 1; - } - - fds[nfds].fd = dhcpv6_socket; - fds[nfds].events = POLLIN; + fds[DHCPV6_FD_INDEX].fd = -1; + fds[DHCPV6_FD_INDEX].events = POLLIN; nfds++; #ifdef WITH_UBUS @@ -503,8 +500,8 @@ int main(_unused int argc, char* const argv[]) syslog(LOG_ERR, "Invalid ubus file descriptor"); return 1; } - fds[nfds].fd = ubus_socket; - fds[nfds].events = POLLIN; + fds[UBUS_FD_INDEX].fd = ubus_socket; + fds[UBUS_FD_INDEX].events = POLLIN; nfds++; #endif /* WITH_UBUS */ @@ -527,6 +524,19 @@ int main(_unused int argc, char* const argv[]) odhcp6c_clear_state(STATE_SIP_FQDN); bound = false; + size_t oro_len = 0; + odhcp6c_get_state(STATE_ORO, &oro_len); + oro_user_cnt = oro_len / sizeof(uint16_t); + + syslog(LOG_NOTICE, "number of user requested options %u", oro_user_cnt); + + if (init_dhcpv6(ifname, client_options, sk_prio, sol_timeout, dscp)) { + syslog(LOG_ERR, "failed to initialize: %s", strerror(errno)); + return 1; + } + + fds[DHCPV6_FD_INDEX].fd = dhcpv6_get_socket(); + syslog(LOG_NOTICE, "(re)starting transaction on %s", ifname); signal_usr1 = signal_usr2 = false; @@ -539,7 +549,7 @@ int main(_unused int argc, char* const argv[]) dhcpv6_set_state(DHCPV6_REQUEST); break; } - + msg_type = DHCPV6_MSG_SOLICIT; dhcpv6_send_request(msg_type); break; @@ -717,10 +727,24 @@ int main(_unused int argc, char* const argv[]) terminate = true; } else { signal_usr2 = false; - dhcpv6_set_state(DHCPV6_INIT); + dhcpv6_set_state(DHCPV6_RESET); } break; + case DHCPV6_RESET: + odhcp6c_clear_state(STATE_CLIENT_ID); + + size_t oro_user_len, oro_total_len; + odhcp6c_get_state(STATE_ORO, &oro_total_len); + oro_user_len = oro_user_cnt * sizeof(uint16_t); + odhcp6c_remove_state(STATE_ORO, oro_user_len, oro_total_len - oro_user_len); + + close(dhcpv6_get_socket()); + fds[DHCPV6_FD_INDEX].fd = -1; + + dhcpv6_set_state(DHCPV6_INIT); + break; + default: break; } @@ -1399,3 +1423,106 @@ void notify_state_change(const char *status, int delay, bool resume) ubus_dhcp_event(status); #endif /* WITH_UBUS */ } + +void config_set_release(bool enable) { + release = enable; +} + +bool config_set_dscp(unsigned int value) { + if (value > 63) + return false; + dscp = value; + return true; +} + +bool config_set_solicit_timeout(unsigned int timeout) { + if (timeout > INT32_MAX) + return false; + + sol_timeout = timeout; + return true; +} + +bool config_set_sk_priority(unsigned int priority) { + if (priority > 6) + return false; + + sk_prio = priority; + return true; +} + +void config_set_client_options(enum dhcpv6_config option, bool enable) { + if (enable) { + client_options |= option; + } else { + client_options &= ~option; + } +} + +bool config_set_request_addresses(char* mode) { + if (!strcmp(mode, "force")) { + ia_na_mode = IA_MODE_FORCE; + allow_slaac_only = -1; + } else if (!strcmp(mode, "none")) { + ia_na_mode = IA_MODE_NONE; + } else if (!strcmp(mode, "try")) { + ia_na_mode = IA_MODE_TRY; + } else { + return false; + } + + return true; +} + +bool config_set_request_prefix(unsigned int length, unsigned int id) { + struct odhcp6c_request_prefix prefix = {0}; + + odhcp6c_clear_state(STATE_IA_PD_INIT); + + if (ia_pd_mode != IA_MODE_FORCE) + ia_pd_mode = length > 128 ? IA_MODE_NONE : IA_MODE_TRY; + + if (length <= 128) { + if (allow_slaac_only >= 0 && allow_slaac_only < 10) + allow_slaac_only = 10; + + prefix.length = length; + prefix.iaid = htonl(id); + + if (odhcp6c_add_state(STATE_IA_PD_INIT, &prefix, sizeof(prefix))) { + syslog(LOG_ERR, "Failed to set request IPv6-Prefix"); + return false; + } + } + + return true; +} + +void config_set_stateful_only(bool enable) { + stateful_only_mode = enable; +} + +void config_clear_requested_options(void) { + oro_user_cnt = 0; +} + +bool config_add_requested_options(unsigned int option) { + if (option > UINT16_MAX) + return false; + + option = htons(option); + if (odhcp6c_insert_state(STATE_ORO, 0, &option, 2)) { + syslog(LOG_ERR, "Failed to add requested option"); + return false; + } + oro_user_cnt++; + return true; +} + +void config_clear_send_options(void) { + odhcp6c_clear_state(STATE_OPTS); +} + +bool config_add_send_options(char* option) { + return (parse_opt(option) == 0); +} diff --git a/src/odhcp6c.h b/src/odhcp6c.h index 740dce7..fc751c6 100644 --- a/src/odhcp6c.h +++ b/src/odhcp6c.h @@ -175,7 +175,8 @@ enum dhcpv6_state { DHCPV6_INFO, DHCPV6_INFO_PROCESSING, DHCPV6_INFO_REPLY, - DHCPV6_EXIT + DHCPV6_EXIT, + DHCPV6_RESET }; enum dhcpv6_status { @@ -471,6 +472,19 @@ int ra_get_retransmit(void); void notify_state_change(const char *status, int delay, bool resume); +void config_set_release(bool enable); +bool config_set_dscp(unsigned int value); +bool config_set_solicit_timeout(unsigned int timeout); +bool config_set_sk_priority(unsigned int value); +void config_set_client_options(enum dhcpv6_config option, bool enable); +bool config_set_request_addresses(char* mode); +bool config_set_request_prefix(unsigned int length, unsigned int id); +void config_set_stateful_only(bool enable); +void config_clear_requested_options(void); +bool config_add_requested_options(unsigned int option); +void config_clear_send_options(void); +bool config_add_send_options(char* option); + int script_init(const char *path, const char *ifname); ssize_t script_unhexlify(uint8_t *dst, size_t len, const char *src); void script_hexlify(char *dst, const uint8_t *src, size_t len); diff --git a/src/ubus.c b/src/ubus.c index 402ba68..71418a4 100644 --- a/src/ubus.c +++ b/src/ubus.c @@ -97,6 +97,23 @@ enum entry_type { ENTRY_PREFIX }; +enum { + RECONFIGURE_DHCP_ATTR_DSCP, + RECONFIGURE_DHCP_ATTR_RELEASE, + RECONFIGURE_DHCP_ATTR_SOL_TIMEOUT, + RECONFIGURE_DHCP_ATTR_SK_PRIORITY, + RECONFIGURE_DHCP_ATTR_OPT_REQUESTED, + RECONFIGURE_DHCP_ATTR_OPT_STRICT, + RECONFIGURE_DHCP_ATTR_OPT_RECONFIGURE, + RECONFIGURE_DHCP_ATTR_OPT_FQDN, + RECONFIGURE_DHCP_ATTR_OPT_UNICAST, + RECONFIGURE_DHCP_ATTR_OPT_SEND, + RECONFIGURE_DHCP_ATTR_REQ_ADDRESSES, + RECONFIGURE_DHCP_ATTR_REQ_PREFIXES, + RECONFIGURE_DHCP_ATTR_STATEFUL, + RECONFIGURE_DHCP_ATTR_MAX, +}; + struct ubus_context *ubus = NULL; static struct blob_buf b; static char ubus_name[24]; @@ -107,11 +124,30 @@ static int ubus_handle_get_stats(struct ubus_context *ctx, struct ubus_object *o struct ubus_request_data *req, const char *method, struct blob_attr *msg); static int ubus_handle_reset_stats(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg); +static int ubus_handle_reconfigure_dhcp(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, struct blob_attr *msg); + +static const struct blobmsg_policy reconfigure_dhcp_policy[RECONFIGURE_DHCP_ATTR_MAX] = { + [RECONFIGURE_DHCP_ATTR_DSCP] = { .name = "dscp", .type = BLOBMSG_TYPE_INT32}, + [RECONFIGURE_DHCP_ATTR_RELEASE] = { .name = "release", .type = BLOBMSG_TYPE_BOOL}, + [RECONFIGURE_DHCP_ATTR_SOL_TIMEOUT] = { .name = "sol_timeout", .type = BLOBMSG_TYPE_INT32}, + [RECONFIGURE_DHCP_ATTR_SK_PRIORITY] = { .name = "sk_prio", .type = BLOBMSG_TYPE_INT32}, + [RECONFIGURE_DHCP_ATTR_OPT_REQUESTED] = { .name = "opt_requested", .type = BLOBMSG_TYPE_ARRAY}, + [RECONFIGURE_DHCP_ATTR_OPT_STRICT] = { .name = "opt_strict", .type = BLOBMSG_TYPE_BOOL}, + [RECONFIGURE_DHCP_ATTR_OPT_RECONFIGURE] = { .name = "opt_reconfigure", .type = BLOBMSG_TYPE_BOOL}, + [RECONFIGURE_DHCP_ATTR_OPT_FQDN] = { .name = "opt_fqdn", .type = BLOBMSG_TYPE_BOOL}, + [RECONFIGURE_DHCP_ATTR_OPT_UNICAST] = { .name = "opt_unicast", .type = BLOBMSG_TYPE_BOOL}, + [RECONFIGURE_DHCP_ATTR_OPT_SEND] = { .name = "opt_send", .type = BLOBMSG_TYPE_ARRAY}, + [RECONFIGURE_DHCP_ATTR_REQ_ADDRESSES] = { .name = "req_addresses", .type = BLOBMSG_TYPE_STRING}, + [RECONFIGURE_DHCP_ATTR_REQ_PREFIXES] = { .name = "req_prefixes", .type = BLOBMSG_TYPE_INT32}, + [RECONFIGURE_DHCP_ATTR_STATEFUL] = { .name = "stateful_only", .type = BLOBMSG_TYPE_BOOL}, +}; static struct ubus_method odhcp6c_object_methods[] = { UBUS_METHOD_NOARG("get_state", ubus_handle_get_state), UBUS_METHOD_NOARG("get_statistics", ubus_handle_get_stats), UBUS_METHOD_NOARG("reset_statistics", ubus_handle_reset_stats), + UBUS_METHOD("reconfigure_dhcp", ubus_handle_reconfigure_dhcp, reconfigure_dhcp_policy), }; static struct ubus_object_type odhcp6c_object_type = @@ -582,6 +618,149 @@ static int ubus_handle_get_state(struct ubus_context *ctx, _unused struct ubus_o return UBUS_STATUS_OK; } +static int ubus_handle_reconfigure_dhcp(_unused struct ubus_context *ctx, _unused struct ubus_object *obj, + _unused struct ubus_request_data *req, _unused const char *method, + struct blob_attr *msg) +{ + const struct blobmsg_policy *policy = reconfigure_dhcp_policy; + struct blob_attr *tb[RECONFIGURE_DHCP_ATTR_MAX]; + struct blob_attr *cur = NULL; + struct blob_attr *elem = NULL; + char *string = NULL; + uint32_t value = 0; + uint32_t index = 0; + bool enabled = false; + bool valid_args = false; + bool need_reinit = false; + + if (blobmsg_parse(policy, RECONFIGURE_DHCP_ATTR_MAX, tb, blob_data(msg), blob_len(msg))) + return UBUS_STATUS_INVALID_ARGUMENT; + + if ((cur = tb[RECONFIGURE_DHCP_ATTR_DSCP])) { + value = blobmsg_get_u32(cur); + if (!config_set_dscp(value)) + return UBUS_STATUS_INVALID_ARGUMENT; + need_reinit = true; + valid_args = true; + } + + if ((cur = tb[RECONFIGURE_DHCP_ATTR_RELEASE])) { + enabled = blobmsg_get_bool(cur); + config_set_release(enabled); + valid_args = true; + } + + if ((cur = tb[RECONFIGURE_DHCP_ATTR_SOL_TIMEOUT])) { + value = blobmsg_get_u32(cur); + if (!config_set_solicit_timeout(value)) + return UBUS_STATUS_INVALID_ARGUMENT; + need_reinit = true; + valid_args = true; + } + + if ((cur = tb[RECONFIGURE_DHCP_ATTR_SK_PRIORITY])) { + value = blobmsg_get_u32(cur); + if (!config_set_sk_priority(value)) + return UBUS_STATUS_INVALID_ARGUMENT; + need_reinit = true; + valid_args = true; + } + + if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_REQUESTED])) { + if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || !blobmsg_check_attr(cur, false)) + return UBUS_STATUS_INVALID_ARGUMENT; + + config_clear_requested_options(); + + blobmsg_for_each_attr(elem, cur, index) { + if (blobmsg_type(elem) != BLOBMSG_TYPE_INT32) + return UBUS_STATUS_INVALID_ARGUMENT; + + value = blobmsg_get_u32(elem); + if (!config_add_requested_options(value)) + return UBUS_STATUS_INVALID_ARGUMENT; + } + + need_reinit = true; + valid_args = true; + } + + if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_STRICT])) { + enabled = blobmsg_get_bool(cur); + config_set_client_options(DHCPV6_STRICT_OPTIONS, enabled); + need_reinit = true; + valid_args = true; + } + + if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_RECONFIGURE])) { + enabled = blobmsg_get_bool(cur); + config_set_client_options(DHCPV6_ACCEPT_RECONFIGURE, enabled); + need_reinit = true; + valid_args = true; + } + + if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_FQDN])) { + enabled = blobmsg_get_bool(cur); + config_set_client_options(DHCPV6_CLIENT_FQDN, enabled); + need_reinit = true; + valid_args = true; + } + + if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_UNICAST])) { + enabled = blobmsg_get_bool(cur); + config_set_client_options(DHCPV6_IGNORE_OPT_UNICAST, enabled); + need_reinit = true; + valid_args = true; + } + + if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_SEND])) { + if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || !blobmsg_check_attr(cur, false)) + return UBUS_STATUS_INVALID_ARGUMENT; + + config_clear_send_options(); + + blobmsg_for_each_attr(elem, cur, index) { + string = blobmsg_get_string(elem); + if (string == NULL || !config_add_send_options(string)) + return UBUS_STATUS_INVALID_ARGUMENT; + } + + need_reinit = true; + valid_args = true; + } + + if ((cur = tb[RECONFIGURE_DHCP_ATTR_REQ_ADDRESSES])) { + string = blobmsg_get_string(cur); + if (string == NULL || !config_set_request_addresses(string)) + return UBUS_STATUS_INVALID_ARGUMENT; + + need_reinit = true; + valid_args = true; + } + + if ((cur = tb[RECONFIGURE_DHCP_ATTR_REQ_PREFIXES])) { + value = blobmsg_get_u32(cur); + + if (!config_set_request_prefix(value, 1)) + return UBUS_STATUS_INVALID_ARGUMENT; + + need_reinit = true; + valid_args = true; + } + + if ((cur = tb[RECONFIGURE_DHCP_ATTR_STATEFUL])) { + enabled = blobmsg_get_bool(cur); + config_set_stateful_only(enabled); + need_reinit = true; + valid_args = true; + } + + if (need_reinit) + raise(SIGUSR2); + + return valid_args ? UBUS_STATUS_OK : UBUS_STATUS_INVALID_ARGUMENT; +} + int ubus_dhcp_event(const char *status) { if (!ubus || !odhcp6c_object.has_subscribers) -- 2.30.2